﻿/*
 * @Autor: PK
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Itinero;
using Itinero.IO.Osm;
using Itinero.Profiles;
using OsmSharp.Streams;
using OsmSharp.Tags;

namespace ShkSimulation.core.util.osm {
	public class OsmDataTransferUtil {
		
		public static OsmRouterData DeserializeRouterDb(string filePath, List<Vehicle> passengerType) {
			FileInfo fileInfo = new FileInfo(filePath);
			ulong memorySize = WindowApiHelper.GetAvailPhys();
			// 512MB
			if (!fileInfo.Exists || (ulong)fileInfo.Length > memorySize - 1024 * 1024 * 512) {
				return OsmRouterData.CreateFailed($"文件太大，剩余内存太小了：{filePath}");
			}
			RouterDb routerDb;
			using (var stream = new FileInfo(filePath).OpenRead()) {
				routerDb = RouterDb.Deserialize(stream);
			}
			string boundInfo;
			if (!routerDb.Meta.TryGetValue(OsmLoadUtil.OsmBounds, out boundInfo)) {
				return OsmRouterData.CreateFailed($"文件不包含边界信息：{filePath}，请重新导入.osm文件使用");
			}
			bool fileNeedUpdate = false;
			foreach (var vehicle in passengerType) {
				if (!routerDb.Supports(vehicle.Name)) {
					routerDb.AddSupportedVehicle(vehicle);
					fileNeedUpdate = true;
				}
			}
			// 如果有不支持的vehicle，则更新文件
			if (fileNeedUpdate) {
				File.Delete(filePath);
				using (var writeRouter = new FileInfo(filePath).Open(FileMode.Create)) {
					routerDb.Serialize(writeRouter);
				}
			}
			// 添加contract
			string contractFile = filePath + @".contract";
			if (File.Exists(contractFile)) {
				using (var contractStream = new FileInfo(contractFile).OpenRead()) {
					routerDb.DeserializeAndAddContracted(contractStream);
				}
			}
			return OsmRouterData.CreateSuccess(new Router(routerDb), routerDb.GetSupportedVehicles().ToList(), Bound.FromString(boundInfo));
		}
		
		public static OsmRouterData TransferOsmToRouter(string osmFilePath, List<Vehicle> passengerType) {
			(Bound box, string message) = GetBoundsEfficient(osmFilePath);
			if (box == null) {
				return OsmRouterData.CreateFailed(message);
			}
			string pbfTempFilePath = osmFilePath + ".pbf";
			if (!TransferOsmToPbf(osmFilePath, pbfTempFilePath)) {
				return OsmRouterData.CreateFailed("Osm文件转换为pbf使失败");
			}
			return CreateRouterByPbf(pbfTempFilePath, passengerType, box);
		}

		public static bool TransferOsmToPbf(string sourceFile, string targetFile) {
			if (!File.Exists(sourceFile)) {
				return false;
			}
			if (File.Exists(targetFile)) {
				return true;
			}
			try {
				using(FileStream reader = new FileInfo(sourceFile).Open(FileMode.Open)){
					using (FileStream writer = new FileInfo(targetFile).Open(FileMode.CreateNew)) {
						using (XmlOsmStreamSource sour = new XmlOsmStreamSource(reader)) {
							PBFOsmStreamTarget pbfOsmStreamTarget = new PBFOsmStreamTarget(writer);
							pbfOsmStreamTarget.RegisterSource(sour);
							pbfOsmStreamTarget.Pull();
							pbfOsmStreamTarget.Flush();
							pbfOsmStreamTarget.Close();
						}
					}
				}
			} catch (Exception e) {
				Console.WriteLine(e);
				return false;
			}
			return true;
		}
		
		public static OsmRouterData CreateRouterByPbf(string pbfFilePath, List<Vehicle> passengerTypes, Bound box) {
			if (!File.Exists(pbfFilePath)) {
				return OsmRouterData.CreateFailed($"Pbf转换失败，文件不存在：{pbfFilePath}");
			}
			string routerFile = pbfFilePath + @".routerdb";
			if (File.Exists(routerFile)) {
				return DeserializeRouterDb(routerFile, passengerTypes);
			}
			try {
				var routerDb = new RouterDb();
				using (var stream = new FileInfo(pbfFilePath).OpenRead()) {
					routerDb.LoadOsmData(stream, passengerTypes.ToArray()); 
				}
				using (var stream = new FileInfo(routerFile).Open(FileMode.Create)) {
					routerDb.Meta.Add(new Tag(OsmLoadUtil.OsmBounds, box.ToString()));
					routerDb.Serialize(stream);
				}
				// using (var stream = new FileInfo(contractFile).Open(FileMode.Create)) {
				// 	foreach (var passengerType in passengerTypes) {
				// 		Profile profile = passengerType.Fastest();
				// 		routerDb.AddContracted(profile);
				// 		routerDb.SerializeContracted(profile, stream);
				// 	}
				// }
				return OsmRouterData.CreateSuccess(new Router(routerDb), passengerTypes, box);
			} catch (Exception e) {
				Console.WriteLine(e);
				return null;
			}
		}
		
		private static (Bound, string) GetBoundsEfficient(string filePath) {
			IEnumerable<string> lines = File.ReadLines(filePath).Take(100);
			string boundLine = null;
			foreach (var line in lines) {
				if (line.Contains(@"<bounds")) {
					boundLine = line;
					break;
				}
			}
			if (boundLine == null) {
				return (null, "请在Osm文件前一百行存放<bounds>标签信息");
			}
			Match match = Regex.Match(boundLine, "minlat=\"(\\d+.\\d+)\" minlon=\"(\\d+.\\d+)\" maxlat=\"(\\d+.\\d+)\" maxlon=\"(\\d+.\\d+)\"");
			if (!match.Success || match.Groups.Count != 5) {
				return (null, "<bounds>标签匹配失败");
			}
			double[] groups = match.Groups.Values.Skip(1).Select(each => Convert.ToDouble(each.Value)).ToArray();
			return (new Bound(groups[0], groups[1], groups[2], groups[3]), "Success");
		}

	}
}